/**
 * @Description:
 * @Author 卞鹏飞 <228443632@qq.com>
 * @create 23/11/24 PM2:00
 */

import {
  onBeforeUnmount,
  shallowRef,
  watch,
  ref,
  unref,
  isRef,
  isReactive,
  type Ref,
  type UnwrapNestedRefs,
  onDeactivated,
  normalizeClass,
  normalizeStyle
} from 'vue'
import Postmate from 'postmate'
import {
  deepClone,
  hasOwn,
  isFunction,
  isPlainObject,
  parseJsonNoError,
  uuid,
  isPrimitive,
  pick,
  debounce
} from 'sf-utils2'
import type { TChildHandshake, TParentOptions, TParentPostmateMountedFunc } from './postmate-types.ts'
import onMountedOrActivated from '@shared/hooks/on-mounted-or-activated'
// import { onMountedOrActivated } from '@shared/hooks/on-mounted-or-activated'

Postmate.debug = process.env.NODE_ENV !== 'production'

/**
 * 获取父窗口hooks
 * @param container
 * @param options
 * @example
 * ``` js
 const containerRef = ref<HTMLHtmlElement>()
 const parentPostmate = ref({
   url: computed(() => props.entry),
   onload: () => {
     loading.value = false
     emit('load')
   }
 })
 const { onParentPostmateMounted, childHandshake, destroy } = usePostmateParent(containerRef, parentPostmate)
 * ```
 */
export function usePostmateParent(
  container: Ref<TParentOptions['container']>,
  options: Ref<TParentOptions> | UnwrapNestedRefs<TParentOptions>
) {
  if (!isRef(options) && !isReactive(options)) console.error('options参数必须是ref或reactive类型')

  const childHandshake = shallowRef<TChildHandshake>()

  const _onQueueMethods = {}

  const handshake = shallowRef()

  const iframe = shallowRef<HTMLIFrameElement>()

  const msgId = ref(uuid())

  // 内部选项
  const _innerOptions = ref<TParentOptions>(deepClone(unref(options)))

  // 挂载回调函数
  const _onParentPostmateMountedCbs = [] as TParentPostmateMountedFunc[]
  /**
   * 父窗口挂在完成时，执行回调
   * @param cb
   */
  const onParentPostmateMounted = (cb: TParentPostmateMountedFunc) => {
    cb && _onParentPostmateMountedCbs.push(cb)
  }

  const defaultOptions = {
    name: `iframe-${uuid().slice(0, 8)}` // Set Iframe name attribute. Useful to get `window.name` in the child.    height: () => document.documentElement.offsetHeight || document.body.offsetHeight
  }
  Object.keys(defaultOptions).forEach((key) => {
    if (!hasOwn(_innerOptions.value, key)) {
      _innerOptions.value[key] = defaultOptions[key]
    }
  })

  const iframeNativeListener = {
    onload() {
      if (_innerOptions.value.onload) {
        _innerOptions.value.onload()
      }
    },

    onerror() {
      if (_innerOptions.value.onerror) {
        _innerOptions.value.onerror()
      }
    }
  }

  type TIframeNativeListenerKey = keyof typeof iframeNativeListener

  /**
   * 刷新
   */
  const refresh = () => {
    if (!container.value) return

    _innerOptions.value.model ||= {}

    // 设置默认的msgId
    if (_innerOptions.value?.model?.msgId) {
      msgId.value = _innerOptions.value.model.msgId
    } else {
      _innerOptions.value.model.msgId = msgId.value
    }

    handshake.value = new Postmate({
      ...pick(_innerOptions.value, (val) => isPlainObject(val) || isPrimitive(val)),
      container: container.value,
      classListArray: normalizeClass(['absolute', 'w-full', 'h-full', ...(_innerOptions.value?.classListArray || [])])
        .split(' ')
        .filter(Boolean)
    })

    iframe.value = container.value.querySelector('iframe') as unknown as HTMLIFrameElement

    const listenerNames = ['onerror', 'onload'] as TIframeNativeListenerKey[]
    listenerNames.forEach((name) => {
      const _name = name.replace(/^on/, '')
      iframe.value.removeEventListener(_name, iframeNativeListener[name])
      iframe.value.addEventListener('load', iframeNativeListener[name])
    })

    iframe.value.style.cssText = normalizeStyle(_innerOptions.value) as string

    handshake.value.then((child) => {
      childHandshake.value = child as TChildHandshake
      const originOn = childHandshake.value.on
      childHandshake.value.on = function () {
        if (arguments[1]) {
          if (isFunction(arguments[1])) {
            const originCb = arguments[1]
            arguments[1] = function () {
              return originCb.call(this, parseJsonNoError(arguments[0] || {}))
            }
          }
        } else {
          arguments[1] = function () {}
        }
        return originOn.call(this, ...arguments)
      }

      Object.keys(_onQueueMethods).forEach((key) => {
        const fn = _onQueueMethods[key]
        childHandshake.value?.on(key, fn)
        delete _onQueueMethods[key]
      })

      // 更新属性
      const attrs = Object.assign({ allowfullscreen: null, frameborder: '0' }, _innerOptions.value?.attrs || {})
      const iframe = container.value.querySelector('iframe')
      Object.keys(attrs).forEach((key) => {
        iframe.setAttribute(key, attrs[key])
      })

      _onParentPostmateMountedCbs.forEach((cb) => cb(childHandshake.value) as TParentPostmateMountedFunc)
    })
  }

  /**
   * 初始化
   */
  const init = () => {
    const url = unref(options)?.url
    const dom = container.value
    if (url && dom) {
      _innerOptions.value.url = url
      destroy()
      refresh()
    }
  }

  /**
   * 初始化
   */
  const debounceInit = debounce(init, 50)

  /**
   * 事件监听
   * @param key
   * @param callback
   */
  const on = (key: string, callback: (...args: any[]) => void) => {
    if (childHandshake.value) {
      if (childHandshake.value?.on) {
        // 如果存在
        childHandshake.value.on(key, callback)
      } else {
        _onQueueMethods[key] = callback
      }
    }
  }

  /**
   * 销毁
   */
  const destroy = () => {
    if (childHandshake.value) {
      childHandshake.value.destroy()
      childHandshake.value = null
    }
  }

  onMountedOrActivated(() => {
    debounceInit()
  })

  onDeactivated(() => {
    destroy()
  })

  onBeforeUnmount(() => {
    destroy()
  })

  watch([() => unref(options)?.url, () => container.value], () => {
    debounceInit()
  })

  return {
    /**
     * 销毁
     */
    destroy,

    /**
     * 监听
     */
    on,

    /**
     *
     */
    handshake,
    /**
     * 子实例
     */
    childHandshake,

    /**
     * 内部选项
     */
    _innerOptions,

    /**
     * 父窗口挂载完成 回调
     */
    onParentPostmateMounted,

    /**
     * iframe实例
     */
    iframe,

    /**
     * 应用的id
     */
    msgId
  }
}
